package com.yn.sample.execution.executor;

import com.yn.sample.domain.ConstantPoolItem;
import com.yn.sample.domain.ConstantPoolItemTypeEnum;
import com.yn.sample.domain.InstructionExecutionResult;
import com.yn.sample.execution.InstructionExecutionContext;
import com.yn.sample.util.OpCodeEnum;
import com.yn.sample.util.ParseEngineHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Objects;

@Component
@Slf4j
public class ExecutorForPutField  implements ExecutorByOpCode{

    @Override
    public String getOpCode() {
        return OpCodeEnum.putfield.name();
    }

    @Override
    public InstructionExecutionResult execute(InstructionExecutionContext context) {
        /**
         * 获取操作数，操作数是常量池的符号引用；比如
         *  6: putfield      #2                  // Field f:I
         *  其中，#2在常量池中：
         *  #2 = Fieldref           #5.#27         // com/yn/sample/CheckAndSet.f:I
         */
        String operand = context.getInstructionVO().getOperand();
        /**
         * 根据operand，查找常量池，找到对应的符号引用
         */
        ConstantPoolItem poolItem = context.getConstantPoolItems().stream().filter(constantPoolItem -> Objects.equals(constantPoolItem.getId(), operand))
                .findFirst().orElse(null);
        if (poolItem == null) {
            throw new RuntimeException();
        }

        boolean b = Objects.equals(poolItem.getConstantPoolItemTypeEnum(), ConstantPoolItemTypeEnum.Fieldref);
        if (!b) {
            throw new RuntimeException();
        }

        /**
         * 一般comment就是这一段内容：
         * com/yn/sample/CheckAndSet.f:I
         */
        String comment = poolItem.getComment();
        String[] classAndFieldName = ParseEngineHelper.getClassAndFieldName(comment);
        if (null == classAndFieldName) {
            throw new RuntimeException();
        }

        Class<?> clazz;
        Field field;
        try {
            clazz = Class.forName(classAndFieldName[0]);
            field = clazz.getDeclaredField(classAndFieldName[1]);
            field.setAccessible(true);
        } catch (ClassNotFoundException | NoSuchFieldException e) {
            log.error("e:{}", e);
            throw new RuntimeException(e);
        }

        /**
         * 从堆栈依次出栈：
         * value，objectref
         */
        Object value = context.getOperandStack().removeLast();
        Object target = context.getOperandStack().removeLast();
        try {
            field.set(target,value);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        return null;
    }
}
